package org.vossnet.sfig;

import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.core.io.ClassPathResource;
import org.antlr.runtime.RecognitionException;

public final class StringUtil {
    private static final Pattern varRplRgx   = Pattern.compile("(\\$\\{([a-zA-Z][a-zA-Z0-9._]*)\\})");
    private static final Pattern regexpMLStr = Pattern.compile("^///\\s*\\r?\\n(.*)///$",Pattern.DOTALL);

    public static Object preprocessString(final String str,final Properties props) {
        return preprocessString(str,props,true);
    }
    public static Object preprocessString(final String str,final Properties props,final boolean unescape) {
        if (str.toLowerCase().startsWith("classpath:")) {
            final CharSequence s = variableReplace(str.substring(str.indexOf(':') + 1),props);
            return new ClassPathResource(unescape ? unescapeString(s).toString() : s.toString());
        }
        final CharSequence s = variableReplace(str,props);
        return unescape ? unescapeString(s).toString() : s.toString();
    }

    public static CharSequence variableReplace(final CharSequence str,final Properties props) {
        if (str != null) {
            final StringBuffer sbuf = new StringBuffer(128);
            final Matcher m = varRplRgx.matcher(str);
            int pos = 0;
            while (m.find()) {
                pos = m.end();
                final String varNm = m.group(2);
                String value = props.getProperty(varNm,System.getProperty(varNm));
                if (value == null)
                    value = m.group(1);
                m.appendReplacement(sbuf, value);
            }
            m.appendTail(sbuf);
            if (sbuf.length() > 0)
                return sbuf;
        }
        return str;
    }

    public static String extractText(final CharSequence input) throws RecognitionException {
        final Matcher m = regexpMLStr.matcher(input);
        if (m.matches())
            return m.group(1);
        throw new RecognitionException();
    }

    /**
     * <p>Unescapes any Java literals found in the <code>String</code>.
     * For example, it will turn a sequence of <code>'\'</code> and
     * <code>'n'</code> into a newline character, unless the <code>'\'</code>
     * is preceded by another <code>'\'</code>.</p>
     * 
     * @param str  the <code>CharSequence</code> to unescape, may be null
     * @return a new unescaped <code>CharSequence</code>, <code>null</code> if null string input
     */
    public static CharSequence unescapeString(final CharSequence str) {
        if (str == null) return null;
        final int sz = str.length();
        if (sz <= 0) return str;
        final StringBuilder sbuf = new StringBuilder(sz);
        final char[] unicode = new char[4];
        boolean hadSlash = false;
        boolean inUnicode = false;
        for (int i = 0, n = 0; i < sz; i++) {
            final char ch = str.charAt(i);
            if (inUnicode) {
                // if in unicode mode, then we're reading unicode values in
                unicode[n++] = ch;
                if (n == 4) {
                    n = 0;
                    // unicode buffer now contains the four hex digits
                    // which represents our unicode character encoding
                    final String unicodeStr = new String(unicode);
                    try {
                        final int value = Integer.parseInt(unicodeStr,16);
                        sbuf.append((char) value);
                        inUnicode = false;
                        hadSlash = false;
                    } catch (NumberFormatException nfe) {
                        // throwing this type of exception matches what the Properties.load() method
                        // does when encountering an invalid unicode encoding in a value string
                        throw new IllegalArgumentException("Unable to parse unicode value: " + unicodeStr, nfe);
                    }
                }
                continue;
            }
            if (hadSlash) {
                // handle an escaped value
                hadSlash = false;
                switch (ch) {
                    case '\\':
                        sbuf.append('\\');
                        break;
                    case '\'':
                        sbuf.append('\'');
                        break;
                    case '\"':
                        sbuf.append('"');
                        break;
                    case 'r':
                        sbuf.append('\r');
                        break;
                    case 'f':
                        sbuf.append('\f');
                        break;
                    case 't':
                        sbuf.append('\t');
                        break;
                    case 'n':
                        sbuf.append('\n');
                        break;
                    case 'b':
                        sbuf.append('\b');
                        break;
                    case 'u':
                        {
                            // uh-oh, we're in unicode country....
                            inUnicode = true;
                            break;
                        }
                    default :
                        sbuf.append(ch);
                        break;
                }
                continue;
            } else if (ch == '\\') {
                hadSlash = true;
                continue;
            }
            sbuf.append(ch);
        }
        if (hadSlash) {
            // then we're in the weird case of a \ at the end of the
            // string, let's output it anyway.
            sbuf.append('\\');
        }
        return sbuf;
    }

}
